#include "fasterdom4fasterxml.h"

#define __DEBUG                     0

#define TRACE_FASTERXML_PROPERTIES(_s_) \
	printf( "%s - xpath[%.*s] propname[%.*s] propvalue[%.*s]\n" , (_s_) , xpath_len,xpath , propname_len,propname , propvalue_len,propvalue ); \

#define TRACE_FASTERXML_PARAMETERS(_s_) \
	printf( "%s - xpath[%.*s] node[%.*s] properties[%.*s] content[%.*s]\n" , (_s_) , xpath_len,xpath , node_len,node , properties_len,properties , content_len,content ); \

struct ParseContext
{
	struct DomNode	*xml_declaration ;
	struct DomNode	*root_node ;
	struct DomNode	*current_parent_node ;
} ;

static inline int CallbackOnXmlProperty( int type , char *xpath , int xpath_len , int xpath_size , char *propname , int propname_len , char *propvalue , int propvalue_len , char *content , int content_len , void *p )
{
	struct DomNode		*node = (struct DomNode *)p ;
	char			*endptr = NULL ;
	enum DomNodeType	node_type ;
	void			*node_data = NULL ;
	int			node_data_len ;
	char			s_buf[ DOMNODE_STRINGCACHE_SIZE ] ;
	int			s_len ;
	char			*s_ptr = NULL ;
	long			l ;
	double			d ;
	struct DomNode		*p_created_node = NULL ;
	
#if __DEBUG
	TRACE_FASTERXML_PROPERTIES( "CallbackOnXmlProperty" )
#endif

	if( propvalue[0] == '\0' )
	{
		node_type = DOMNODE_TYPE_STRING ;
		node_data = propvalue ;
		node_data_len = 0 ;
	}
	else if( ( '0' <= propvalue[0] && propvalue[0] <= '9' ) || ( propvalue[1] == '-' && '0' <= propvalue[1] && propvalue[1] <= '9' ) )
	{
		if( memchr( propvalue , '.' , content_len ) )
		{
			node_type = DOMNODE_TYPE_DOUBLE ;
			d = strtod( propvalue , & endptr ) ;
			if( endptr-propvalue != propvalue_len )
			{
				goto _GOTO_STRING_TYPE;
			}
			node_data = & d ;
			node_data_len = -1 ;
		}
		else
		{
			node_type = DOMNODE_TYPE_LONG ;
			l = strtol( propvalue , & endptr , 10 ) ;
			if( endptr-propvalue != propvalue_len )
			{
				goto _GOTO_STRING_TYPE;
			}
			node_data = & l ;
			node_data_len = -1 ;
		}
	}
	else
	{
_GOTO_STRING_TYPE :
		node_type = DOMNODE_TYPE_STRING ;
		if( propvalue_len < DOMNODE_STRINGCACHE_SIZE )
		{
			XMLUNESCAPE_FOLD( propvalue , propvalue_len , s_buf , s_len , sizeof(s_buf)-1 )
			if( s_len < -1 )
				return -31;
			node_data = s_buf ;
			node_data_len = s_len ;
		}
		else
		{
			s_ptr = (char*)malloc( propvalue_len+1 ) ;
			if( s_ptr == NULL )
				return -41;
			XMLUNESCAPE_FOLD( propvalue , propvalue_len , s_ptr , s_len , propvalue_len+1 )
			if( s_len < -1 )
				return -42;
			node_data = s_ptr ;
			node_data_len = CREATEDOMNODE_REFERENCE_AND_DELEGATION_OF_FREE ;
		}
	}
	
	p_created_node = CreateDomNode( propname , propname_len , node_type , node_data , node_data_len ) ;
	if( p_created_node == NULL )
	{
		return -71;
	}
	
	LinkXmlPropertyDomNode( node , p_created_node );
	
	return 0;
}

static inline int CallbackOnXmlNode( int type , char *xpath , int xpath_len , int xpath_size , char *node , int node_len , char *properties , int properties_len , char *content , int content_len , void *p )
{
	struct ParseContext	*p_parse_ctx = (struct ParseContext *)p ;
	char			*endptr = NULL ;
	enum DomNodeType	node_type ;
	void			*node_data = NULL ;
	int			node_data_len ;
	char			s_buf[ DOMNODE_STRINGCACHE_SIZE ] ;
	int			s_len ;
	char			*s_ptr = NULL ;
	long			l ;
	double			d ;
	struct DomNode		*p_created_node = NULL ;
	int			nret = 0 ;
	
	if( type & FASTERXML_NODE_BRANCH )
	{
		if( type & FASTERXML_NODE_ENTER )
		{
#if __DEBUG
			TRACE_FASTERXML_PARAMETERS( "CallbackOnXmlNode - FASTERXML_NODE_ENTER" )
#endif
			
			if( p_parse_ctx->root_node == NULL )
			{
				p_parse_ctx->current_parent_node = CreateDomNode( node , node_len , DOMNODE_TYPE_BRANCH , NULL , -1 ) ;
				if( p_parse_ctx->current_parent_node == NULL )
				{
					return -11;
				}
				p_parse_ctx->root_node = p_parse_ctx->current_parent_node ;
				
				if( p_parse_ctx->xml_declaration && GetDomNodeXmlDeclarationPtr(p_parse_ctx->root_node) == NULL )
					SetDomNodeXmlDeclaration( p_parse_ctx->root_node , p_parse_ctx->xml_declaration );
			}
			else
			{
				struct DomNode	*current_parent_node = NULL ;
				
				current_parent_node = AddChildDomNode_BRANCH( p_parse_ctx->current_parent_node , node , node_len ) ;
				if( current_parent_node == NULL )
				{
					return -12;
				}
				p_parse_ctx->current_parent_node = current_parent_node ;
			}
			
			if( properties_len > 0 )
			{
				nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnXmlProperty , (void*)(p_parse_ctx->current_parent_node) ) ;
				if( nret )
					return -13;
			}
		}
		else if( type & FASTERXML_NODE_LEAVE )
		{
#if __DEBUG
			TRACE_FASTERXML_PARAMETERS( "CallbackOnXmlNode - FASTERXML_NODE_LEAVE" )
#endif

			p_parse_ctx->current_parent_node = GetParentDomNode(p_parse_ctx->current_parent_node) ;
		}
		else
		{
#if __DEBUG
			TRACE_FASTERXML_PARAMETERS( "CallbackOnXmlNode - FASTERXML_NODE_BRANCH" )
#endif

			if( p_parse_ctx->root_node == NULL )
			{
				if( p_parse_ctx->xml_declaration == NULL )
				{
					p_parse_ctx->xml_declaration = CreateDomNode( node , node_len , DOMNODE_TYPE_BRANCH , NULL , 0 ) ;
					if( p_parse_ctx->xml_declaration == NULL )
					{
						return -31;
					}
					
					if( properties_len > 0 )
					{
						nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnXmlProperty , (void*)(p_parse_ctx->xml_declaration) ) ;
						if( nret )
							return -32;
					}
				}
			}
			else
			{
				struct DomNode	*current_node = NULL ;
				
				current_node = AddChildDomNode_BRANCH( p_parse_ctx->current_parent_node , node , node_len ) ;
				if( current_node == NULL )
				{
					return -41;
				}
				
				if( properties_len > 0 )
				{
					nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnXmlProperty , (void*)current_node ) ;
					if( nret )
						return -42;
				}
			}
		}
	}
	else if( type & FASTERXML_NODE_LEAF )
	{
#if __DEBUG
		TRACE_FASTERXML_PARAMETERS( "CallbackOnXmlNode - FASTERXML_NODE_LEAF" )
#endif

		if( content[0] == '\0' )
		{
			node_type = DOMNODE_TYPE_STRING ;
			node_data = content ;
			node_data_len = 0 ;
		}
		else if( type == FASTERXML_NODE_LEAF_CDATA )
		{
			node_type = DOMNODE_TYPE_STRING ;
			node_data = content ;
			node_data_len = content_len ;
		}
		else if( ( '0' <= content[0] && content[0] <= '9' ) || ( content[1] == '-' && '0' <= content[1] && content[1] <= '9' ) )
		{
			if( memchr( content , '.' , content_len ) )
			{
				node_type = DOMNODE_TYPE_DOUBLE ;
				d = strtod( content , & endptr ) ;
				if( endptr-content != content_len )
				{
					goto _GOTO_STRING_TYPE;
				}
				node_data = & d ;
				node_data_len = -1 ;
			}
			else
			{
				node_type = DOMNODE_TYPE_LONG ;
				l = strtol( content , & endptr , 10 ) ;
				if( endptr-content != content_len )
				{
					goto _GOTO_STRING_TYPE;
				}
				node_data = & l ;
				node_data_len = -1 ;
			}
		}
		else
		{
_GOTO_STRING_TYPE :
			node_type = DOMNODE_TYPE_STRING ;
			if( content_len < DOMNODE_STRINGCACHE_SIZE )
			{
				XMLUNESCAPE_FOLD( content , content_len , s_buf , s_len , sizeof(s_buf)-1 )
				if( s_len < -1 )
					return -31;
				node_data = s_buf ;
				node_data_len = s_len ;
			}
			else
			{
				s_ptr = (char*)malloc( content_len+1 ) ;
				if( s_ptr == NULL )
					return -41;
				XMLUNESCAPE_FOLD( content , content_len , s_ptr , s_len , content_len+1 )
				if( s_len < -1 )
					return -42;
				node_data = s_ptr ;
				node_data_len = CREATEDOMNODE_REFERENCE_AND_DELEGATION_OF_FREE ;
			}
		}
		
		if( type == FASTERXML_NODE_LEAF_CDATA )
			p_created_node = CreateXmlCdataDomNode( node , node_len , node_data , node_data_len ) ;
		else
			p_created_node = CreateDomNode( node , node_len , node_type , node_data , node_data_len ) ;
		if( p_created_node == NULL )
		{
			return -41;
		}
		
		LinkChildDomNode( p_parse_ctx->current_parent_node , p_created_node );
		
		if( properties_len > 0 )
		{
			nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnXmlProperty , (void*)p_created_node ) ;
			if( nret )
				return -13;
		}
	}
	
	return 0;
}

struct DomNode *ParseXmlToDom( char *xml )
{
	struct ParseContext	parse_ctx ;
	
	int			nret = 0 ;
	
	memset( & parse_ctx , 0x00 , sizeof(struct ParseContext) );
	nret = TravelXmlBuffer( xml , NULL , -1 , CallbackOnXmlNode , & parse_ctx ) ;
	SetParserLastError( nret );
	if( nret )
	{
		DestroyAllDomNodes( parse_ctx.root_node );
		return NULL;
	}
	else
	{
		return parse_ctx.root_node;
	}
}

static inline int AppendXmlDumpBuffer( char **pp_buf , int *p_buf_size , int *p_buf_len , char *src , int src_len )
{
	int	dst_len ;
	int	nret = 0 ;
	
	if( src_len == -1 )
		src_len = strlen(src) ;
	while(1)
	{
		XMLESCAPE_EXPAND( src , src_len , (*pp_buf)+(*p_buf_len) , dst_len , (*p_buf_size)-1-(*p_buf_len) )
		if( dst_len >= 0 )
		{
			(*p_buf_len) += dst_len ;
			return 0;
		}
		
		nret = ResizeDumpBuffer( pp_buf , p_buf_size ) ;
		if( nret )
			return nret;
	}
}

static inline void DumpDepth( char **pp_buf , int *p_buf_size , int *p_buf_len , int dump_options , int depth )
{
	int		i ;
	
	if( dump_options == DUMPOPTIONS_INDENTATION )
	{
		for( i = 0 ; i < depth ; i++ )
		{
			AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\t" );
		}
	}
	
	return;
}

static int DumpXmlPropertyDomNodes( struct DomNode *node , char **pp_buf , int *p_buf_size , int *p_buf_len , int dump_options , int depth )
{
	char			*node_name = NULL ;
	struct DomNode		*prop_node = NULL ;
	enum DomNodeType	node_type ;
	char			*s = NULL ;
	long			l ;
	double			d ;
	enum DomNodeExist	is_exist ;
	
	prop_node = GetFirstXmlPropertyDomNode( node ) ;
	while( prop_node )
	{
		node_name = GetDomNodeName( prop_node ) ;
		node_type = GetDomNodeType( prop_node ) ;
		switch( node_type )
		{
			case DOMNODE_TYPE_STRING :
				s = GetDomNodeData_string( prop_node , & is_exist ) ;
				if( is_exist == DOMNODE_NODE_DATA_EXIST )
				{
					AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , " " );
					AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
					AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "=\"" );
					AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , s , -1 );
					AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\"" );
				}
				break;
			case DOMNODE_TYPE_LONG :
				l = GetDomNodeData_long( prop_node , & is_exist ) ;
				if( is_exist == DOMNODE_NODE_DATA_EXIST )
				{
					char	buf[ 40 + 1 ] ;
					int	buf_len ;
					memset( buf , 0x00 , sizeof(buf) );
					buf_len = snprintf( buf , sizeof(buf)-1 , "%ld" , l ) ;
					
					AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , " " );
					AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
					AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "=\"" );
					AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , buf , buf_len );
					AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\"" );
				}
				break;
			case DOMNODE_TYPE_DOUBLE :
				d = GetDomNodeData_double( prop_node , & is_exist ) ;
				if( is_exist == DOMNODE_NODE_DATA_EXIST )
				{
					char	buf[ 80 + 1 ] ;
					int	buf_len ;
					memset( buf , 0x00 , sizeof(buf) );
					buf_len = snprintf( buf , sizeof(buf)-1 , "%lf" , d ) ;
					
					AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , " " );
					AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
					AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "=\"" );
					AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , buf , buf_len );
					AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\"" );
				}
				break;
			default :
				return -1;
		}
		
		prop_node = GetNextXmlPropertyDomNode( prop_node ) ;
	}
	
	return 00;
}

static int DumpDomNodes( struct DomNode *nodes , char **pp_buf , int *p_buf_size , int *p_buf_len , int dump_options , int depth )
{
	struct DomNode		*node = GetFirstDomNode( nodes ) ;
	char			*node_name = NULL ;
	enum DomNodeType	node_type ;
	char			*s = NULL ;
	long			l ;
	double			d ;
	enum DomNodeExist	is_exist ;
	
	while( node )
	{
		node_name = GetDomNodeName( node ) ;
		node_type = GetDomNodeType( node ) ;
		
		if( GetChildrenDomNodes(node) )
		{
#if __DEBUG
			printf( "DumpDomNodes - [%s] is branch\n" , node_name );
#endif
			
			DumpDepth( pp_buf , p_buf_size , p_buf_len , dump_options , depth );
			AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "<" );
			AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
			DumpXmlPropertyDomNodes( node , pp_buf , p_buf_size , p_buf_len , dump_options , depth );
			AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , ">" );
			if( dump_options == DUMPOPTIONS_INDENTATION )
			{
				AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\n" );
			}
			
			switch( node_type )
			{
				case DOMNODE_TYPE_BRANCH :
					DumpDomNodes( GetChildrenDomNodes(node) , pp_buf , p_buf_size , p_buf_len , dump_options , depth+1 );
					break;
				default :
					break;
			}
			
			DumpDepth( pp_buf , p_buf_size , p_buf_len , dump_options , depth );
			AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "</" );
			AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
			AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , ">" );
			if( dump_options == DUMPOPTIONS_INDENTATION )
			{
				AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\n" );
			}
		}
		else if( GetDomNodeDataExist(node) == DOMNODE_NODE_DATA_NOT_EXIST )
		{
#if __DEBUG
			printf( "DumpDomNodes - [%s] is DOMNODE_NODE_DATA_NOT_EXIST\n" , node_name );
#endif
			
			DumpDepth( pp_buf , p_buf_size , p_buf_len , dump_options , depth );
			if( depth < 0 )
			{
				AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "<?" );
				AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
				DumpXmlPropertyDomNodes( node , pp_buf , p_buf_size , p_buf_len , dump_options , depth );
				AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "?>" );
			}
			else
			{
				AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "<" );
				AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
				DumpXmlPropertyDomNodes( node , pp_buf , p_buf_size , p_buf_len , dump_options , depth );
				AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , " />" );
			}
			
			if( dump_options == DUMPOPTIONS_INDENTATION )
			{
				AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\n" );
			}
		}
		else
		{
#if __DEBUG
			printf( "DumpDomNodes - [%s] is leaf\n" , node_name );
#endif
			
			switch( node_type )
			{
				case DOMNODE_TYPE_STRING :
					s = GetDomNodeData_string( node , & is_exist ) ;
					if( is_exist == DOMNODE_NODE_DATA_EXIST )
					{
						DumpDepth( pp_buf , p_buf_size , p_buf_len , dump_options , depth );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "<" );
						AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
						DumpXmlPropertyDomNodes( node , pp_buf , p_buf_size , p_buf_len , dump_options , depth );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , ">" );
						if( IsXmlCdataDomNode(node) == 0 )
						{
							AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , s , -1 );
						}
						else
						{
							AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "<![CDATA[" );
							AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , s );
							AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "]]>" );
						}
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "</" );
						AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , ">" );
						if( dump_options == DUMPOPTIONS_INDENTATION )
						{
							AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\n" );
						}
					}
					break;
				case DOMNODE_TYPE_LONG :
					l = GetDomNodeData_long(node , & is_exist ) ;
					if( is_exist == DOMNODE_NODE_DATA_EXIST )
					{
						char	buf[ 40 + 1 ] ;
						int	buf_len ;
						memset( buf , 0x00 , sizeof(buf) );
						buf_len = snprintf( buf , sizeof(buf)-1 , "%ld" , l ) ;
						
						DumpDepth( pp_buf , p_buf_size , p_buf_len , dump_options , depth );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "<" );
						AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
						DumpXmlPropertyDomNodes( node , pp_buf , p_buf_size , p_buf_len , dump_options , depth );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , ">" );
						AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , buf , buf_len );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "</" );
						AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , ">" );
						if( dump_options == DUMPOPTIONS_INDENTATION )
						{
							AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\n" , 1 );
						}
					}
					break;
				case DOMNODE_TYPE_DOUBLE :
					d = GetDomNodeData_double(node , & is_exist ) ;
					if( is_exist == DOMNODE_NODE_DATA_EXIST )
					{
						char	buf[ 80 + 1 ] ;
						int	buf_len ;
						memset( buf , 0x00 , sizeof(buf) );
						buf_len = snprintf( buf , sizeof(buf)-1 , "%lf" , d ) ;
						
						DumpDepth( pp_buf , p_buf_size , p_buf_len , dump_options , depth );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "<"  );
						AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
						DumpXmlPropertyDomNodes( node , pp_buf , p_buf_size , p_buf_len , dump_options , depth );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , ">" );
						AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , buf , buf_len );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "</" );
						AppendXmlDumpBuffer( pp_buf , p_buf_size , p_buf_len , node_name , -1 );
						AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , ">" );
						if( dump_options == DUMPOPTIONS_INDENTATION )
						{
							AppendDumpBuffer( pp_buf , p_buf_size , p_buf_len , "\n" );
						}
					}
					break;
				default :
					break;
			}
		}
		
		node = GetNextDomNode( node ) ;
	}
	
	return 0;
}

char *DumpDomToXml( struct DomNode *root , int dump_options )
{
	char		*p_buf = NULL ;
	int		buf_size ;
	int		buf_len ;
	struct DomNode	*xml_declaration = NULL ;
	int		nret = 0 ;
	
	if( root == NULL )
		return NULL;
	
	nret = CreateDumpBuffer( & p_buf , & buf_size , & buf_len ) ;
	if( nret )
		return NULL;
	
	xml_declaration = GetDomNodeXmlDeclarationPtr(root) ;
	if( xml_declaration )
	{
		nret = DumpDomNodes( xml_declaration , & p_buf , & buf_size , & buf_len , dump_options , -1 ) ;
		if( nret )
		{
			free( p_buf );
			return NULL;
		}
	}
	
	nret = DumpDomNodes( root , & p_buf , & buf_size , & buf_len , dump_options , 0 ) ;
	if( nret )
	{
		free( p_buf );
		return NULL;
	}
	
	return p_buf;
}

